package top.lywivan.netty.reactor;


import top.lywivan.netty.reactor.cnxn.ServerCnxn;
import top.lywivan.netty.reactor.model.Request;
import top.lywivan.netty.reactor.model.RequestData;
import top.lywivan.netty.reactor.model.ResponseData;
import top.lywivan.netty.reactor.serialize.RequestSerializer;
import top.lywivan.netty.reactor.serialize.ResponseSerializer;
import top.lywivan.netty.reactor.serialize.Serializer;

import java.nio.ByteBuffer;
import java.security.SecureRandom;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;

public class BusinessService {

    private final String threadNamePrefix;
    private final int numBusinessThreads;

    private final Server server;

    private ExecutorService executorService;


    public BusinessService(Server server, int numBusinessThreads, String threadNamePrefix) {
        this.server = server;
        this.threadNamePrefix = threadNamePrefix;
        this.numBusinessThreads = numBusinessThreads;
        this.executorService = Executors.newFixedThreadPool(numBusinessThreads,new BusinessThreadFactory(threadNamePrefix));
    }


    /**
     * 提交请求
     * @param request
     */
    public void submitRequest(Request request) {
        BusinessHandler handler = new BusinessHandler(request);
        executorService.execute(handler);
    }



    public class BusinessHandler implements Runnable {

        private final ServerCnxn cnxn;

        private final Request request;

        public BusinessHandler( Request request) {
            this.request = request;
            this.cnxn = request.getCnxn();
        }

        @Override
        public void run() {
            try {
                Serializer serializer = server.getSerializer();

                ByteBuffer reqBuf = this.request.getRequest();
                byte[] bytes = new byte[reqBuf.remaining()];
                reqBuf.get(bytes);
                RequestData requestData = null;
                if (serializer instanceof RequestSerializer) {
                    RequestSerializer requestSerializer = (RequestSerializer) serializer;
                    requestData = requestSerializer.decodeRequest(bytes);
                }
                System.out.println("服务端接收到的请求数据为:"+ requestData);
                SecureRandom secureRandom = new SecureRandom();
                int i = secureRandom.nextInt(2000);
                System.out.println("即将模拟请求执行耗费" + i  +"毫秒");
                Thread.currentThread().sleep(i);

                //回写数据
                ResponseData response = new ResponseData();
                response.setId(requestData.getId());
                response.setStatus(200);
                response.setMsg("hello " + i);
                System.out.println("服务端要返回的响应为:"+response);

                if (serializer instanceof ResponseSerializer) {
                    ResponseSerializer rs = (ResponseSerializer) serializer;
                    byte[] rsbytes = rs.encodeResponse(response);
                    ByteBuffer buffer = ByteBuffer.allocate(4+rsbytes.length);
                    buffer.putInt(rsbytes.length);
                    buffer.put(rsbytes);
                    buffer.flip();
                    //发送buffer
                    cnxn.sendBuffer(buffer);

                    // TODO throttled
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    private class BusinessThreadFactory implements ThreadFactory {

        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;

        public BusinessThreadFactory(String namePrefix) {
            this.namePrefix = namePrefix;
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
        }

        @Override
        public Thread newThread(Runnable r) {
            Thread ioThread = new Thread(group,r,namePrefix+"-"+threadNumber.getAndIncrement());
            if (!ioThread.isDaemon()) {
                ioThread.setDaemon(true);
            }
            if (ioThread.getPriority() != Thread.NORM_PRIORITY) {
                ioThread.setPriority(Thread.NORM_PRIORITY);
            }
            return ioThread;
        }
    }

}
